package com.ming_za.odupdaterserver.core.controller.web;

import com.ming_za.odupdaterserver.core.config.ConfigManager;
import com.ming_za.odupdaterserver.core.config.type.BooleanValue;
import com.ming_za.odupdaterserver.core.config.type.IntValue;
import com.ming_za.odupdaterserver.core.exception.ROEnum;
import com.ming_za.odupdaterserver.core.utils.Msg;
import com.ming_za.odupdaterserver.core.utils.NetworkUtils;
import com.ming_za.odupdaterserver.core.utils.VersionUtils;
import com.ming_za.odupdaterserver.core.utils.account.AccountManager;
import com.ming_za.odupdaterserver.core.utils.account.AccountRE;
import com.ming_za.odupdaterserver.core.utils.account.exception.AccountException;
import com.ming_za.odupdaterserver.core.utils.account.param.ReturnAccountPackage;
import com.ming_za.odupdaterserver.core.vo.R;
import com.ming_za.odupdaterserver.core.vo.req.LoginVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.Getter;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Tag(name = "账号API")
@RestController
@RequestMapping("/account")
public class AccountAPI {

    private final AccountManager accountManager;
    private boolean needLogin;
    private int tryPasswordErrorCount;
    private long lockTime;
    private boolean frequentRemoteLock;

    private long lastErrorTime = 0;
    private int nowErrorCount = 0;
    private int frCount = 0;
    private long fLastLoginTime = 0;
    @Getter
    private boolean fLocked = false;

    private final List<String> fIp = new ArrayList<>();

    public AccountAPI(AccountManager accountManager, ConfigManager configManager) {
        this.accountManager = accountManager;

        BooleanValue loginVerification = configManager.getLoginVerification();
        this.needLogin = loginVerification.getValue();
        loginVerification.bindListener((v)->{
            this.needLogin = v;
        });

        IntValue allowPasswordErrorCount = configManager.getAllowPasswordErrorCount();
        this.tryPasswordErrorCount = allowPasswordErrorCount.getValue();
        allowPasswordErrorCount.bindListener((v)->{
           this.tryPasswordErrorCount = v;
        });

        IntValue verificationLockTime = configManager.getVerificationLockTime();
        this.lockTime = verificationLockTime.getValue() * 60000L;
        verificationLockTime.bindListener((v)->{
            this.lockTime = v * 60000L;
        });

        BooleanValue frequentRemoteLock1 = configManager.getFrequentRemoteLock();
        this.frequentRemoteLock = frequentRemoteLock1.getValue();
        frequentRemoteLock1.bindListener((v)->{
            this.frequentRemoteLock = v;
        });
    }

    @Operation(summary = "登录")
    @PostMapping("/login")
    public R<?> login(HttpServletRequest request, HttpServletResponse response, @RequestBody @Valid LoginVo loginVo){

        if (!needLogin && NetworkUtils.notLocalHost(request.getRemoteHost())){
//            throw new BusinessException(ROEnum.USER_REMOTE_LOGIN_ERROR);
            return R.pack(ROEnum.USER_REMOTE_LOGIN_ERROR);
        }

        if (tryPasswordErrorCount != 0 && nowErrorCount >= tryPasswordErrorCount){
            //触发锁定
            if (lockTime == 0){
//                throw new BusinessException(ROEnum.USER_LOGIN_LOCK,"登录已锁定，请到服务端控制台解锁");
                Msg.getInstance().warn("[" + request.getRemoteHost() + "]登录失败！原因：频繁登录失败，已触发锁定！");
                return R.pack(ROEnum.USER_LOGIN_LOCK,"登录已锁定，请到服务端控制台解锁");
            }
            long nowTime = System.currentTimeMillis();
            if (nowTime - lastErrorTime <= lockTime){
//                throw new BusinessException(ROEnum.USER_LOGIN_LOCK,"登录已锁定，请" + (lockTime / 60000) + "分钟后重试");
                int min = (int) (lockTime / 60000);
                Msg.getInstance().warn("[" + request.getRemoteHost() + "]登录失败！原因：频繁登录失败，已触发锁定！需在" + min + "分钟后重试");
                return R.pack(ROEnum.USER_LOGIN_LOCK,"登录已锁定，请" + min + "分钟后重试");
            }
        }

        if (fLocked){
            Msg.getInstance().warn("[安全警告]频繁异地登录！登录已锁定。相关ip->" + Arrays.toString(fIp.toArray(new String[0])));
//            throw new BusinessException(ROEnum.USER_LOGIN_LOCK,"登录已锁定，请到服务端控制台解锁");
            return R.pack(ROEnum.USER_LOGIN_LOCK,"登录已锁定，请到服务端控制台解锁");
        }

        try {
            String lastIp = accountManager.getAllAccount().get(0).getLastIp();

            String nowIp = request.getRemoteHost();

            ReturnAccountPackage<String> token = accountManager.loginAccount(loginVo.getPassword(),nowIp);

            if (!token.isSuccess()){
                //登录失败
                if (token.getCode() == AccountRE.WRONG_ACCOUNT_OR_PASSWORD){
                    //密码错误时
                    if (tryPasswordErrorCount != 0){
                        long nowTime = System.currentTimeMillis();
                        //十分钟间隔，十分钟内错误累加1
                        if (nowTime - lastErrorTime > 600000){
                            nowErrorCount = 0;
                        }
                        nowErrorCount++;
                        lastErrorTime = nowTime;
                    }
                }

                ROEnum ae = ROEnum.account(token.getCode());
                Msg.getInstance().warn("[" + request.getRemoteHost() + "]登录失败！原因：" + ae.getDefaultMsg());
                return R.pack(ae,token.getData());
            }

            response.addHeader("token",token.getData());

            //频繁异地登录检测
            if (frequentRemoteLock){
                Msg.getInstance().warn("[" + request.getRemoteHost() + "]异地登录！已记录。上次登录的ip：" + lastIp);
                if (lastIp != null && !nowIp.equals(lastIp)){
                    //开启频繁异地登录检测，半小时内异地登录累加
                    long nowTime = System.currentTimeMillis();
                    if (nowTime - fLastLoginTime > 1800000){
                        frCount = 0;
                        fIp.clear();
                    }
                    if (!fIp.contains(nowIp)){
                        fIp.add(nowIp);
                    }
                    frCount++;
                    fLastLoginTime = nowTime;
                    if (frCount > 2){
                        fLocked = true;
                    }
                }
            }

        } catch (AccountException e) {
//            throw new BusinessException(e.getMessage(),ROEnum.UNEXPECTED_ERROR);
            return R.pack(ROEnum.UNEXPECTED_ERROR);
        }

        Msg.getInstance().info("&a" + request.getRemoteHost() + "登录成功！");

        return R.ok();
    }

    /**
     * 解除锁定
     */
    public void unLockLogin(){
        lastErrorTime = 0;
        nowErrorCount = 0;
        frCount = 0;
        fLastLoginTime = 0;
        fLocked = false;
        fIp.clear();
    }

    @Operation(summary = "检查是否需要登录",description = "返回true表示不需要登录，否则需要登录")
    @GetMapping("/login")
    public R<Boolean> checkDontLogin(HttpServletRequest request,HttpServletResponse response){

        boolean result = true;

        if (needLogin){
            ReturnAccountPackage<String> token = accountManager.checkToken(request.getHeader("token"), request.getRemoteHost());

            if (token.isSuccess()){
                response.addHeader("token",token.getData());
            }else {
                result = false;
            }
        }else if (NetworkUtils.notLocalHost(request.getRemoteHost())){
//            throw new BusinessException(ROEnum.USER_REMOTE_LOGIN_ERROR);
            return R.pack(ROEnum.USER_REMOTE_LOGIN_ERROR);
        }

        return R.ok(result);

//        boolean result = false;
//
//        if (needLogin){
//            try {
//                String newToken = accountManager.checkToken(request.getHeader("token"),request.getRemoteHost());
//                response.addHeader("token",newToken);
//            } catch (AccountException ignored) {
//            }
//            result = true;
//        }else if (NetworkUtils.notLocalHost(request.getRemoteHost())){
//            throw new BusinessException(ROEnum.USER_REMOTE_LOGIN_ERROR);
//        }
//
//        return R.ok(result);
    }

}
